#include "Math.inc"
#include "GlobalParameters.inc"
#include "OrenNayerLighting.inc"

/////////////
// GLOBALS //
/////////////

cbuffer PerObjectBuffer //: register( b1 )
{
	float4x4 World;	//: packoffset( c0 )
	float cubeSize;	//: packoffset( c1 )
};

DepthStencilState depthStencil;

struct VS_Input{
	float4 Position : POSITION;
	float4 Color : TEXCOORD0;	
};

struct GS_Input{	
	float4 Position : POSITION;
	float4 Color : TEXCOORD0;	
};

struct PS_Input{
	float4 Position : SV_POSITION;
	float4 Normal : NORMAL;
	float4 Color : TEXCOORD0;
};

// Vxl vertex shader
GS_Input VxlVertexShader(VS_Input vsIn){
	GS_Input vsOut = (GS_Input) 0;
	
	vsOut.Position = vsIn.Position;
	vsOut.Color = vsIn.Color;
	
	return vsOut;
}

//
// Takes in a Vxl and outputs a single cube 
//
[maxvertexcount(24)]
void VxlGeometryShader(point GS_Input input[1], inout TriangleStream<PS_Input> cubeStream )
{
	float4x4 WVP = mul(mul( World, View), Projection);
	PS_Input output;

	float4 topLeftFront = mul(float4( float3(-0.5f, 0.5f, -0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);
	float4 bottomLeftFront = mul(float4( float3(-0.5f, -0.5f, -0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);
	float4 topRightFront = mul(float4( float3(0.5f, 0.5f, -0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);
	float4 bottomRightFront = mul(float4( float3(0.5f, -0.5f, -0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);
	float4 topLeftBack = mul(float4( float3(-0.5f, 0.5f, 0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);
	float4 topRightBack = mul(float4( float3(0.5f, 0.5f, 0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);
	float4 bottomLeftBack = mul(float4( float3(-0.5f, -0.5f, 0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);
	float4 bottomRightBack = mul(float4( float3(0.5f, -0.5f, 0.5f) * cubeSize + input[0].Position.xyz, 1.0f), WVP);

	float4 frontNormal = mul(float4(0.0f, 0.0f, 1.0f, 0.0f), World);
	float4 backNormal = mul(float4(0.0f, 0.0f, -1.0f, 0.0f), World);
	float4 topNormal = mul(float4(0.0f, 1.0f, 0.0f, 0.0f), World);
	float4 bottomNormal = mul(float4(0.0f, -1.0f, 0.0f, 0.0f), World);
	float4 leftNormal = mul(float4(-1.0f, 0.0f, 0.0f, 0.0f), World);
	float4 rightNormal = mul(float4(1.0f, 0.0f, 0.0f, 0.0f), World);

	float4 normals[6] = 		
	{
		frontNormal ,	
		bottomNormal ,	
		backNormal,		
		topNormal,		
		leftNormal,		
		rightNormal
	};

	float4 cubePoints[24] = 
	{
		bottomLeftFront,	topLeftFront,			bottomRightFront,	topRightFront,		// Front 
		bottomLeftFront,	bottomRightFront,		bottomLeftBack,		bottomRightBack,	// Bottom 
		bottomLeftBack,		bottomRightBack,		topLeftBack,		topRightBack,		// Back
		topLeftFront,		topLeftBack,			topRightFront,		topRightBack,		// Top 
		bottomLeftBack,		topLeftBack,			bottomLeftFront,	topLeftFront,	// Left
		bottomRightFront,	topRightFront,			bottomRightBack,	topRightBack		// Right
	};

	//[unroll]
	for(int i=0; i<6; i++)
	{	
		output.Position = cubePoints[i*4];
		output.Normal = normals[i];
		output.Color = input[0].Color;		
		
		cubeStream.Append(output);

		output.Position = cubePoints[i*4+1];
		cubeStream.Append(output);
		
		output.Position = cubePoints[i*4+2];
		cubeStream.Append(output);

		output.Position = cubePoints[i*4+3];
		cubeStream.Append(output);
		
		cubeStream.RestartStrip();	    
	}	
}

float4 VxlPixelShader(PS_Input psIn): SV_Target
{	
	return psIn.Color * Light_OrenNayer(psIn.Normal.xyz, CameraPos.xyz, LightDirection.xyz);
}


DepthStencilState ED
{
	DepthEnable = TRUE;	
	DepthWriteMask = ALL; 
	DepthFunc = LESS;
};

technique10 VxlTechnique
{
	pass
	{
		SetVertexShader( CompileShader( vs_4_0, VxlVertexShader() ) );
		SetGeometryShader ( CompileShader( gs_4_0, VxlGeometryShader() ));
		SetPixelShader( CompileShader( ps_4_0, VxlPixelShader() ) );
		SetDepthStencilState( ED, 0);

		SetRasterizerState(SOLID);
		SetBlendState( NoAlphaBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
	}
};